package com.dynamic.datasource.factory;

import com.dynamic.datasource.constants.Constant;
import com.dynamic.datasource.property.DataSourceProperty;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;

import javax.sql.DataSource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;

/**
 * @author 王先堂
 * @Description hikari数据源工厂
 * @createTime 2022/03/24/ 10:43:00
 */
public class HikariDataSourceFactory extends AbstractDataSourceFactory implements InitializingBean {

    private static Method configCopyMethod = null;

    static {
        fetchMethod();
    }

    /**
     * 全局的配置
     */
    private HikariConfig config;

    /**
     * to support springboot 1.5 and 2.x
     * HikariConfig 2.x use 'copyState' to copy config
     * HikariConfig 3.x use 'copyStateTo' to copy config
     */
    @SuppressWarnings("JavaReflectionMemberAccess")
    private static void fetchMethod() {
        try {
            configCopyMethod = HikariConfig.class.getMethod("copyState", HikariConfig.class);
            return;
        } catch (NoSuchMethodException ignored) {
        }
        try {
            configCopyMethod = HikariConfig.class.getMethod("copyStateTo", HikariConfig.class);
            return;
        } catch (NoSuchMethodException ignored) {
        }
        throw new RuntimeException("HikariConfig does not has 'copyState' or 'copyStateTo' method!");
    }

    @Override
    public DataSource doCreate(DataSourceProperty dataSourceProperty) {
        /**
         * 当前数据源配置会覆盖全局配置
         */
        HikariConfig hikariConfig = new HikariConfig();
        if (Objects.nonNull(dataSourceProperty.getHikari())) {
            hikariConfig = dataSourceProperty.getHikari();
        } else if (Objects.nonNull(config)) {
            hikariConfig = config;
        }
        hikariConfig.setUsername(dataSourceProperty.getUsername());
        hikariConfig.setPassword(dataSourceProperty.getPassword());
        hikariConfig.setJdbcUrl(dataSourceProperty.getUrl());
        hikariConfig.setPoolName(dataSourceProperty.getName());
        String driverClassName = dataSourceProperty.getDriverClassName();
        if (!StringUtils.isEmpty(driverClassName)) {
            hikariConfig.setDriverClassName(driverClassName);
        }
        //是否延迟创建
        if (Boolean.FALSE.equals(dataSourceProperty.getLazy())) {
            return new HikariDataSource(hikariConfig);
        }
        hikariConfig.validate();
        HikariDataSource dataSource = new HikariDataSource();
        try {
            configCopyMethod.invoke(hikariConfig, dataSource);
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException("HikariConfig failed to copy to HikariDataSource", e);
        }
        return dataSource;
    }

    @Override
    public Boolean isSupport(DataSourceProperty dataSourceProperty) {
        Class<? extends DataSource> type = dataSourceProperty.getType();
        return Objects.nonNull(type) && Constant.HIKARI_DATASOURCE.equals(type.getName());
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        config = globalDataSourceProperty.getHikari();
    }
}
